package com.wei.czz.framework.common.helper;

import com.alibaba.fastjson2.JSON;
import com.wei.czz.common.dto.common.NameValue;
import com.wei.czz.common.dto.common.sms.SendSmsDto;
import com.wei.czz.common.enums.CommonEnum;
import com.wei.czz.common.enums.ResultEnum;
import com.wei.czz.common.enums.common.dict.DictEnum;
import com.wei.czz.common.enums.common.sms.SmsChannelEnum;
import com.wei.czz.common.exception.CzzException;
import com.wei.czz.common.po.common.sms.SendSmsPo;
import com.wei.czz.common.utils.DataUtils;
import com.wei.czz.common.utils.RegexUtils;
import com.wei.czz.common.utils.SecurityUtils;
import com.wei.czz.common.po.common.sms.BatchSendSmsPo;
import com.wei.czz.common.utils.TimeUtils;
import com.wei.czz.framework.common.entity.DictEntity;
import com.wei.czz.framework.common.entity.SmsLogEntity;
import com.wei.czz.framework.common.entity.SmsTemplateEntity;
import com.wei.czz.framework.common.service.*;
import com.wei.czz.framework.common.wrapper.DictWrapper;
import lombok.AllArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.*;

/**
 * Created by IntelliJ IDEA.
 *
 * @author wyw
 * date: 2023-06-14 14:20:41
 * className: SmsSendHelper 短信发送帮助类
 * version: 1.0
 * description:
 */
@Component
@AllArgsConstructor
public class SmsSendHelper {

    private static final Logger log = LoggerFactory.getLogger(SmsSendHelper.class);

    /**
     * 短信渠道映射
     * key：  SmsChannelEnum类枚举值
     * value：SmsService子类
     */
    private static final Map<Integer, SmsService> map = new HashMap<>();

    private final SmsTemplateService smsTemplateService;

    private final SmsLogService smsLogService;

    private final AesService aesService;

    private final AsyncService asyncService;

    private final DictWrapper dictWrapper;

    /**
     * 注册短信实现类
     * @param channel    短信渠道
     * @param smsService 短信服务对象
     */
    public static void register(Integer channel, SmsService smsService) {
        synchronized (map) {
            map.put(channel, smsService);
        }
    }

    /**
     * 发送短信操作
     * @param sendSmsPo 参数对象
     * @return 发送结果
     */
    public SendSmsDto sendSms(SendSmsPo sendSmsPo) {

        String mobile = sendSmsPo.getMobile();
        if (!RegexUtils.isPhone(mobile)) {
            log.error("接收手机号格式错误。mobile={}", mobile);
            throw new CzzException(ResultEnum.MOBILE_FAIL);
        }

        /*
            校验单ip发送短信次数
         */
        this.checkIpSendSms(sendSmsPo.getAccount());

        /*
            获取短信模板
         */
        SmsTemplateEntity smsTemplate = smsTemplateService.find(sendSmsPo.getType());
        if (Objects.isNull(smsTemplate)) {
            throw new CzzException(ResultEnum.NOT_SMS_TEMPLATE);
        }

        /*
            获取短信渠道服务
         */
        SmsService smsService = this.getSmsService(smsTemplate.getChannel());

        /*
            校验短信参数
         */
        smsService.validParamMap(smsTemplate.getParams(), sendSmsPo.getParamMap());

        SendSmsDto sendSmsDto = null;
        try {

            /*
                发送短信
             */
            sendSmsDto = smsService.sendSms(mobile, smsTemplate, sendSmsPo.getParamMap());

            return sendSmsDto;
        } finally {
            SendSmsDto _sendSmsDto = sendSmsDto;

            /*
                添加短信发送日志
             */
            asyncService.execute(() -> {
                // 密文
                String encryptMobile = aesService.encryptToHex(sendSmsPo.getMobile());
                // 脱敏
                String maskMobile = DataUtils.dataMask(mobile);
                // 后缀加密
                String mobileSuffix = aesService.encryptToHex(mobile.substring(mobile.length() - 4));

                // 实际发送内容构造
                String content = this.buildContent(smsTemplate.getChannel(), smsTemplate.getContent(),
                        sendSmsPo.getParamMap(), false);
                // 短信内容加密
                content = aesService.czzEncrypt(content);

                // 参数序列化
                String param = Optional.ofNullable(sendSmsPo.getParamMap())
                        .map(JSON::toJSONString)
                        .map(aesService::czzEncrypt)
                        .orElse(StringUtils.EMPTY);

                // 发送结果
                String result = StringUtils.EMPTY;
                Integer status = CommonEnum.ONE.getValue();
                String remark = StringUtils.EMPTY;
                if (Objects.nonNull(_sendSmsDto)) {
                    if (!CollectionUtils.isEmpty(_sendSmsDto.getResultMap())) {
                        result = JSON.toJSONString(_sendSmsDto.getResultMap());
                        result = aesService.czzEncrypt(result);
                    }
                    status = _sendSmsDto.getStatus();
                    if (CommonEnum.ONE.getValue().equals(status)) {
                        // 发送失败，把失败描述放到描述中，可以直接查看
                        remark = _sendSmsDto.getMsg();
                    }
                }

                Long optUserId = SecurityUtils.getOrDefaultUserId(CommonEnum.ZERO.getLongValue());
                String optUsername = SecurityUtils.getOrDefaultUsername(StringUtils.EMPTY);
                Date date = new Date();

                // 保存短信发送记录
                SmsLogEntity smsLog = new SmsLogEntity();
                smsLog.setAccount(sendSmsPo.getAccount())
                        .setMobile(encryptMobile)
                        .setSubMobile(maskMobile)
                        .setMobileSuffix(mobileSuffix)
                        .setType(sendSmsPo.getType())
                        .setVersion(smsTemplate.getVersion())
                        .setChannel(smsTemplate.getChannel())
                        .setContent(content)
                        .setParam(param)
                        .setResult(result)
                        .setStatus(status)
                        .setDeleteStatus(CommonEnum.ZERO.getValue())
                        .setUpdateTime(date)
                        .setUpdateUser(StringUtils.EMPTY)
                        .setUpdateUserId(CommonEnum.ZERO.getLongValue())
                        .setCreateTime(date)
                        .setCreateUser(optUsername)
                        .setCreateUserId(optUserId)
                        .setRemark(remark);
                smsLogService.saveOrEdit(smsLog);
            });
        }

    }

    public SendSmsDto batchSendSms(BatchSendSmsPo batchSendSmsPo) {

        List<String> mobileList = batchSendSmsPo.getMobileList();

        String errorMobile = mobileList.stream()
                .filter(mobile -> !RegexUtils.isPhone(mobile))
                .findFirst()
                .orElse(null);
        if (Objects.nonNull(errorMobile)) {
            log.error("批量发送短信，手机号格式错误。mobile={}", errorMobile);
            throw new CzzException(ResultEnum.MOBILE_FAIL);
        }

        /*
            校验单ip发送短信次数
         */
        this.checkIpSendSms(batchSendSmsPo.getAccount());

        /*
            获取短信模板
         */
        SmsTemplateEntity smsTemplate = smsTemplateService.find(batchSendSmsPo.getType());

        /*
            获取短信渠道服务
         */
        SmsService smsService = this.getSmsService(smsTemplate.getChannel());

        /*
            校验短信参数
         */
        smsService.validParamMap(smsTemplate.getParams(), batchSendSmsPo.getParamMap());

        SendSmsDto sendSmsDto = null;
        try {

            /*
                发送短信
             */
            sendSmsDto = smsService.batchSendSms(mobileList, smsTemplate, batchSendSmsPo.getParamMap());

            return sendSmsDto;
        } finally {
            SendSmsDto _sendSmsDto = sendSmsDto;

            /*
                添加短信发送日志
             */
            asyncService.execute(() -> {
                // 实际发送内容构造
                String content = this.buildContent(smsTemplate.getChannel(), smsTemplate.getContent(),
                        batchSendSmsPo.getParamMap(), false);
                // 短信内容加密
                content = aesService.czzEncrypt(content);

                // 参数序列化
                String param = Optional.ofNullable(batchSendSmsPo.getParamMap())
                        .map(JSON::toJSONString)
                        .map(aesService::czzEncrypt)
                        .orElse(StringUtils.EMPTY);

                // 发送结果
                String result = StringUtils.EMPTY;
                Integer status = CommonEnum.ONE.getValue();
                String remark = StringUtils.EMPTY;
                if (Objects.nonNull(_sendSmsDto)) {
                    if (!CollectionUtils.isEmpty(_sendSmsDto.getResultMap())) {
                        result = JSON.toJSONString(_sendSmsDto.getResultMap());
                        result = aesService.czzEncrypt(result);
                    }
                    status = _sendSmsDto.getStatus();
                    if (CommonEnum.ONE.getValue().equals(status)) {
                        remark = _sendSmsDto.getMsg();
                    }
                }

                Long optUserId = SecurityUtils.getOrDefaultUserId(CommonEnum.ZERO.getLongValue());
                String optUsername = SecurityUtils.getOrDefaultUsername(StringUtils.EMPTY);
                Date date = new Date();

                // 批量保存短信发送记录
                List<SmsLogEntity> smsLogList = new ArrayList<>();
                for (String mobile : mobileList) {
                    // 加密
                    String encryptMobile = aesService.encryptToHex(mobile);
                    // 脱敏
                    String subMobile = DataUtils.dataMask(mobile);
                    // 后缀加密
                    String mobileSuffix = aesService.encryptToHex(mobile.substring(mobile.length() - 4));

                    SmsLogEntity smsLog = new SmsLogEntity();
                    smsLog.setAccount(batchSendSmsPo.getAccount())
                            .setMobile(encryptMobile)
                            .setSubMobile(subMobile)
                            .setMobileSuffix(mobileSuffix)
                            .setType(batchSendSmsPo.getType())
                            .setVersion(smsTemplate.getVersion())
                            .setChannel(smsTemplate.getChannel())
                            .setContent(content)
                            .setParam(param)
                            .setResult(result)
                            .setStatus(status)
                            .setDeleteStatus(CommonEnum.ZERO.getValue())
                            .setUpdateTime(date)
                            .setUpdateUser(StringUtils.EMPTY)
                            .setUpdateUserId(CommonEnum.ZERO.getLongValue())
                            .setCreateTime(date)
                            .setCreateUser(optUsername)
                            .setCreateUserId(optUserId)
                            .setRemark(remark);
                    smsLogList.add(smsLog);
                }
                smsLogService.batchSave(smsLogList);
            });
        }
    }

    /**
     * 构造短信发送内容
     * @param channel  短信渠道
     * @param content  短信模板
     * @param paramMap 参数映射
     * @return 完整短信内容
     */
    public String buildContent(Integer channel, String content, Map<String, String> paramMap, boolean highlight) {
        // 获取短信渠道服务
        SmsService smsService = this.getSmsService(channel);

        // 构造
        return smsService.buildContent(content, paramMap, highlight);
    }

    /**
     * 获取短信渠道参数操作
     * @param channel 短信渠道
     * @return 短信参数映射
     */
    public Map<String, NameValue<String>> getChannelParam(Integer channel, String channelParam) {
        // 获取短信渠道服务
        SmsService smsService = this.getSmsService(channel);
        // 获取短信渠道参数映射
        return smsService.getChannelParam(channelParam);
    }

    /**
     * 获取短信渠道服务
     * @param channel 短信渠道
     * @return 短信渠道服务对象
     */
    public SmsService getSmsService(Integer channel) {
        // 获取短信渠道服务
        SmsService smsService = map.get(channel);
        if (Objects.isNull(smsService)) {
            log.warn("短信模板对应的短信渠道不存在。{}", SmsChannelEnum.get(channel));
            throw new CzzException(ResultEnum.NOT_SMS_CHANNEL);
        }
        return smsService;
    }

    /**
     * 校验单ip发送短信次数
     * @param account 账号
     */
    private void checkIpSendSms(String account) {
        if (!RegexUtils.isIp(account)) {
            log.debug("发送短信，请求账号不是ip地址，无需校验发送次数。account={}", account);
            return;
        }
        // 当天0点时间
        Date startDate = TimeUtils.startToDay();

        /*
            获取短信发送记录
         */
        List<SmsLogEntity> smsLogList = smsLogService.findList(account, null, null, null,
                startDate, null);
        if (!smsLogList.isEmpty()) {
            /*
                单ip一天允许发送短信次数字典
             */
            DictEntity dict = dictWrapper.getOneDict(DictEnum.IP_MAX_SMS);

            int max;
            try {
                max = Integer.parseInt(dict.getValue());
            } catch (NumberFormatException e) {
                log.error("{}字典值配置错误。{} message={}", DictEnum.IP_MAX_SMS.getName(), dict, e.getMessage());
                max = 20;
            }
            if (smsLogList.size() >= max) {
                throw new CzzException(ResultEnum.REQUEST_FAIL.getCode(), "单自然日最多允许发送" + max + "次短信");
            }
        }
    }

}
